/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

public class Commands extends Command {
	private Vector commands = new Vector();
	private static long start_time_milli;
	private static Date start_time_date;
	private static SimpleDateFormat simple_date_time_format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	private static Commands command_instance = null;
	// set the echo on and off
	private static boolean echo_off = false;
	// Sets the prompt at the current directory
	private VectorString directory = new VectorString();
	// The  input sets from the user
	private static VectorString user_inputs = new VectorString();

	// Some variables on the system information and application version

	static {
		command_instance = new Commands();
		command_instance.addCommand(command_instance);
		//commands.add(new Commands());
	}

	public static Commands getInstance() {
		return command_instance;
	}

	public Commands() {
		start_time_date = new java.util.Date();
		start_time_milli = System.currentTimeMillis();
		directory.add(getGroupName());
	}

	public VectorString getDirectory() {
		return directory;
	}

	public static String getPrompt() {
		String prompt = "";
		for (int i = 0;i < command_instance.getDirectory().size();i++) {
			prompt += command_instance.getDirectory().get(i) + "/";
		}
		return prompt.substring(0, prompt.length() - 1) + "> ";
	}

	public static VectorString getUserInputs() {
		return user_inputs;
	}

	public void addCommand(Command command) {
		commands.add(command);
	}

	public String getGroupName() {
		return "Root";
	}

	public void printSplashScreen(OutputStream output_stream)
	throws IOException {
		output_stream.write("\r\n\r\n\r\n".getBytes());
		output_stream.write("*****************************************************\r\n".getBytes());
		output_stream.write(("*            Welcome To VRMS " + getVersion() + "            *\r\n").getBytes());
		output_stream.write("*                                                   *\r\n".getBytes());
		output_stream.write("*****************************************************\r\n\r\n".getBytes());
	}

	private Command findCommandByGroup(String group_name) {
		for (int i = 0;i < commands.size();i++) {
			Command command = (Command) commands.get(i);
			if (command.getGroupName().equalsIgnoreCase(group_name) || (group_name.indexOf("*") != -1 && command.getGroupName().length() >= group_name.indexOf("*") && command.getGroupName().substring(0, group_name.indexOf("*")).equalsIgnoreCase(group_name.substring(0, group_name.indexOf("*"))))) {
				return command;
			}
		}
		return null;
	}

	private void chagngeDirectory(String p_directory) {
		if (p_directory.indexOf("cd ..") != -1 && directory.size() > 1) {
			directory.remove(directory.size() - 1);
			return ;
		}
		String new_directory = doesDirectoryExist(directory + "/" +  p_directory.substring(p_directory.indexOf("cd ") + "cd ".length()));
		if (new_directory != null) {
			directory = new VectorString(new StringTokenizer((new_directory.trim()),"/"));
		}
	}

	public VectorString execute(VectorString p_directory, String cmd,OutputStream output_stream) {
		directory = p_directory;
		if (directory.size() == 0) {
			directory.add(getGroupName());
		}
		try {
			cmd = directory.buildNewStringFromPositionSeperatedBy(0, "/") + "/" + cmd + " ";
			if (cmd.indexOf("cd ") != -1) {
				chagngeDirectory(cmd);
				return directory;
			}
			invokeCommand(cmd, output_stream);
		} catch (Exception exception) {
			exception.printStackTrace();
			try {
				output_stream.write((exception.getMessage() + "\r\n").getBytes());
			} catch (Exception e) {}
		}
		return directory;
	}
	/**
	 *
	 */
	public void invokeCommand(String cmd, OutputStream output_stream)
	throws Exception {
		String group = cmd.substring(0, cmd.indexOf(" "));
		String sub_group = cmd.substring(cmd.indexOf(" ") + 1);
		if (group.indexOf("/") != -1) {
			group = group.substring(0, group.indexOf("/"));
			sub_group = cmd.substring(cmd.indexOf("/") + 1);
		}
		// Check if group is an Command
		if (findCommandByGroup(group) != null) {
			findCommandByGroup(group).invokeCommand(sub_group + " ", output_stream);
			return ;
		} else {
			// It must be a method
			if (findMethod("command" + group) != null) {
				String args[] = buildArguments(sub_group);
				Object objects[] = new Object[2];
				objects[0] = args;objects[1] = output_stream;
				findMethod("command" + group).invoke(this, objects);
				return ;
			}
		}
		commandHelp(null, output_stream);

	}

	/**
	 * This displays a list of all the current methods, and all the classes that are contained
	 */
	public void commandHelp(String args[], OutputStream output_stream)
	throws Exception {
		output_stream.write(("<"+getGroupName()+"> has the following sub directories\r\n").getBytes());
		for (int i = 0;i < commands.size();i++) {
			Command command = (Command) commands.get(i);
			if (!command.getGroupName().equals(getGroupName())) {
				output_stream.write(("\t" + command.getGroupName() + "\r\n").getBytes());
			}
		}
		output_stream.write(("<"+getGroupName()+"> has the following executables\r\n").getBytes());
		output_stream.write((listCommands() + "\r\n").getBytes());
		output_stream.write("To change directories type cd <driectory name>\r\n".getBytes());
		output_stream.write("To move to the parent directory type cd ..\r\n".getBytes());
		output_stream.write("To execute a command just type the name of the command, and optional parameters\r\n\r\n".getBytes());
	}

	public String getVersion() {
		return "Version 1.0";
	}

	/**
	 * This displays information on the Command System (Memory available ext)
	 * This can include arguments to isolate the information it gives, otherwise it defaults to all information
	 */
	public void commandSystem(String args[], OutputStream output_stream)
	throws Exception {
		if (args == null || args.length == 0 || args[0].equals("-Help") || args[0].equals("-help")) {
			output_stream.write("Help\r\n".getBytes());
			output_stream.write("\t-version              Current running version\r\n".getBytes());
			output_stream.write("\t-start_time_milli     Start time (in milli seconds)\r\n".getBytes());
			output_stream.write("\t-running_time_milli   Total running time\r\n".getBytes());
			output_stream.write("\t-start_time_time      Start time\r\n".getBytes());
			output_stream.write("\t-system_gc            Perform a Garbage Collection\r\n\r\n".getBytes());
			return ;
		}
		for (int i = 0;i < args.length;i++) {
			if (args[i].equals("-version")) {
				output_stream.write((getVersion() + "\r\n").getBytes());
			}
			if (args[i].equals("-start_time_milli")) {
				output_stream.write(("Start Time (in milli seconds) " + start_time_milli + "\r\n").getBytes());
			}
			if (args[i].equals("-running_time_milli")) {
				output_stream.write(("Running time (in milli seconds) " + (System.currentTimeMillis() - start_time_milli) + "\r\n").getBytes());
			}
			if (args[i].equals("-start_time_time")) {
				output_stream.write(("Start Time " + simple_date_time_format.format(start_time_date) + "\r\n").getBytes());
			}
			if (args[i].equals("-system_gc")) {
				output_stream.write(("Memory before garbage collection [" + Runtime.getRuntime().freeMemory() + "].").getBytes());
				System.gc();
				output_stream.write(("After garbage collection [" + Runtime.getRuntime().freeMemory() + "]\r\n").getBytes());
			}
		}
	}

	/**
	 * Displays information on the systems memory
	 */
	public void commandMemory(String args[], OutputStream output_stream)
	throws Exception {
		output_stream.write(("Total Memory      :" + Runtime.getRuntime().totalMemory() + "\r\n").getBytes());
		output_stream.write(("Free  Memory      :" + Runtime.getRuntime().freeMemory() + "\r\n\r\n").getBytes());
	}

	public static boolean isEchoOn() {
		return !echo_off;
	}

	/**
	 * This is will turn the echo back functionality off
	 */
	public void commandEchoOff(String args[], OutputStream output_stream)
	throws Exception {
		echo_off = true;
		output_stream.write("Echo has been turned off\r\n".getBytes());
	}

	/**
	 * This is will turn the echo back functionality off
	 */
	public void commandEchoOn(String args[], OutputStream output_stream)
	throws Exception {
		echo_off = false;
		output_stream.write("Echo has been turned on\r\n".getBytes());
	}

	/**
	 * This will try to shut the system down
	 * If it is safe then it will
	 */
	public void commandShutdown(String args[], OutputStream output_stream)
	throws Exception {
		if (isSafeToStop()) {
			Log.trace.out( "System shutdown by monitor at [" + simple_date_time_format.format(new java.util.Date()) + "]", "");
			output_stream.write(("System shutdown by monitor at [" + simple_date_time_format.format(new java.util.Date()) + "]\r\n").getBytes());
			System.exit(0);
		} else {
			output_stream.write(("The System is not is a safe state to shutdown. There are still sensitive processors active\r\n").getBytes());
		}
	}

	public boolean isSafeToStop() {
		boolean may_shutdown = true;
		for (int i = 0;i < commands.size();i++) {
			if (!((Command)commands.get(i)).getGroupName().equals(getGroupName()) && !((Command)commands.get(i)).isSafeToStop()) {
				may_shutdown = false;
			}
		}
		return may_shutdown;
	}

	/**
	 * The Settings command where you can update settings
	 */
	public void commandSettings(String args[], OutputStream output_stream)
	throws Exception {
		if (args == null || args.length == 0) {
			args = new String[]{"-help"};
		}
		for (int i = 0;i < args.length;i++) {
			if (args[i].equalsIgnoreCase("-help")) {
				output_stream.write("Settings help\r\n".getBytes());
				output_stream.write("\t-list <name>         Lists all the settings. If no name is specified then lists all\r\n".getBytes());
				output_stream.write("\t-set <name> <value>  Sets the setting identifued by <name> to <value>\r\n".getBytes());
				return ;
			}
			if (args[i].equalsIgnoreCase("-list")) {
				output_stream.write("List\r\n".getBytes());
				Enumeration e=Settings.getSettingNames();
				while (e.hasMoreElements()){
					String s=""+e.nextElement();
					if (args.length <= i + 1 || s.indexOf(args[i + 1]) != -1) {
						output_stream.write(("\t" + s + "\t " + Settings.getString(s, "") + "\r\n").getBytes());
					}
				}
			}
			if (args[i].equalsIgnoreCase("-set")) {
				if (args.length < i + 2) {
					output_stream.write("You need to specify two parameters for the set command. See help\r\n".getBytes());
					return ;
				}
				boolean exists=false;
				for (Enumeration e=Settings.getSettingNames();e.hasMoreElements() && !exists;exists=args[i + 1].equals(e.nextElement()));

				if (!exists) {
					output_stream.write(("The name [" + args[i + 1] + "] you specified does not exist. See list\r\n").getBytes());
					return ;
				}
				Settings.setString(args[i + 1], args[i + 2]);
				Settings.save();
				output_stream.write((args[i + 1] + "] set to " + args[i + 2] + "\r\n").getBytes());
			}
		}
	}

	/**
	 * Does directory exist (does the method or the command exist)
	 */
	public String doesDirectoryExist(String p_directory) {
		try {
			// seperated by '.' could be getGroupName.methodname || root.other_class
			VectorString arguments = new VectorString(new StringTokenizer(p_directory, "/"));
			arguments.remove(0);
			if (arguments.size() < 1) {
				return null;
			}
				if (findCommandByGroup(arguments.get(0)) != null) {
					if (arguments.size() > 1) {
						String new_directory = findCommandByGroup(arguments.get(0)).doesDirectoryExist(arguments.buildNewStringFromPosition(1));
						if (new_directory != null) {
							return directory + "/" + new_directory;
						}
					} else {
						return directory + "/" + findCommandByGroup(arguments.get(0)).getGroupName();
					}
				}
		} catch (Exception exception) {
			exception.printStackTrace();
		}
		return null;
	}

}

